﻿using MongoDB.Bson.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using IT.CMS.Core.Domain.Blogs;
using IT.CMS.Core.Domain.Sites;
using IT.CMS.Core;
using IT.CMS.Core.Domain.Configuration;
using IT.CMS.Core.Domain.Directory;
using IT.CMS.Core.Domain.Tax;
using IT.CMS.Core.Domain.Localization;
using IT.CMS.Core.Domain.Customers;
using IT.CMS.Core.Domain.Catalog;
using IT.CMS.Core.Domain.Seo;
using IT.CMS.Core.Domain.Messages;
using IT.CMS.Core.Domain.Forums;
using IT.CMS.Core.Domain.Discounts;
using IT.CMS.Core.Domain.Topics;
using IT.CMS.Core.Domain.News;
using IT.CMS.Core.Domain.Shipping;
using IT.CMS.Core.Domain.Logging;
using IT.CMS.Core.Domain.Tasks;
using IT.CMS.Core.Domain.Polls;
using IT.CMS.Core.Domain.Common;
using IT.CMS.Core.Domain.Security;
using MongoDB.Bson.Serialization.Options;
using IT.CMS.Core.Domain.Media;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Bson.IO;
using MongoDB.Bson;
using IT.CMS.Core.Domain.Orders;
using System.Reflection;

namespace IT.CMS.Data.Mapping
{

    public class DateTimeSerializer : BsonBaseSerializer
    {
        public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
        {
            Nullable<DateTime> dateTime = null;
            var bsonType = bsonReader.CurrentBsonType;
            switch (bsonType)
            {
                case BsonType.Null:
                    bsonReader.ReadNull();
                    return dateTime;
                case BsonType.DateTime:
                    dateTime = new BsonDateTime(bsonReader.ReadDateTime()).ToUniversalTime();
                    return dateTime;

                default:
                    return dateTime;
                    // var message = string.Format("DateTimeSerializer needs a DateTime not {0}.", bsonType);
                    // throw new BsonSerializationException(message);
            }
        }

        public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
        {
            DateTime utcDateTime;

            if (value == null)
            {
                TimeSpan nowMs = DateTime.Now - new DateTime(1970, 1, 1);
                // bsonWriter.WriteDateTime((long)nowMs.TotalMilliseconds);
                bsonWriter.WriteNull();
            }
            else
            {
                var dateTime = (DateTime)value;
                utcDateTime = BsonUtils.ToUniversalTime(dateTime);
                var millisecondsSinceEpoch = BsonUtils.ToMillisecondsSinceEpoch(utcDateTime);
                bsonWriter.WriteDateTime(millisecondsSinceEpoch);
            }
        }
    }


    public class DateTimeMapper : ICustomBsonTypeMapper
    {
        public bool TryMapToBsonValue(object value, out BsonValue bsonValue)
        {
            throw new NotImplementedException();
        }
    }


    public static class RegisterClassMaps
    {
        public static void RegisterDominClassMaps()
        {

            // BsonTypeMapper.RegisterCustomTypeMapper(typeof(System.Nullable<DateTime>), new DateTimeMapper());
            //foreach (Type T in AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsSubclassOf(typeof(BaseEntity))))
            //{
            //}

            BsonClassMap.RegisterClassMap<BaseEntity>(cm =>
            {
                cm.AutoMap();
                cm.SetIsRootClass(true);
                cm.MapIdProperty(c => c.Id);
            });





            
            BsonClassMap.RegisterClassMap<ProductVariant>(cm =>
            {
                
                cm.AutoMap();
                cm.SetDiscriminator("ProductVariant");
                
                Type type = typeof(ProductVariant);
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") &&  p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }

                //cm.GetMemberMap(mc => mc.AvailableStartDateTimeUtc).SetSerializer(new DateTimeSerializer());
                //cm.GetMemberMap(mc => mc.AvailableEndDateTimeUtc).SetSerializer(new DateTimeSerializer());

                cm.UnmapProperty(pv => pv.BackorderMode);
                cm.UnmapProperty(pv => pv.DownloadActivationType);
                cm.UnmapProperty(pv => pv.GiftCardType);
                cm.UnmapProperty(pv => pv.LowStockActivity);
                cm.UnmapProperty(pv => pv.ManageInventoryMethod);
                cm.UnmapProperty(pv => pv.RecurringCyclePeriod);

            });


            ////ProductVariant
            //BsonClassMap.RegisterClassMap<ProductVariant>(cm =>
            //{
            //    cm.AutoMap();
            //    cm.SetDiscriminator("ProductVariant");

            //    cm.GetMemberMap(mc => mc.AvailableStartDateTimeUtc).SetSerializer(new DateTimeSerializer());
            //    cm.GetMemberMap(mc => mc.AvailableEndDateTimeUtc).SetSerializer(new DateTimeSerializer());

            //    cm.UnmapProperty(pv => pv.BackorderMode);
            //    cm.UnmapProperty(pv => pv.DownloadActivationType);
            //    cm.UnmapProperty(pv => pv.GiftCardType);
            //    cm.UnmapProperty(pv => pv.LowStockActivity);
            //    cm.UnmapProperty(pv => pv.ManageInventoryMethod);
            //    cm.UnmapProperty(pv => pv.RecurringCyclePeriod);

            //});
            //BlogPost
            BsonClassMap.RegisterClassMap<BlogPost>(cm => { 
                cm.AutoMap(); 
                cm.SetDiscriminator("BlogPost");
                Type type = typeof(BlogPost);
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }

            });



            //RecurringPayment
            BsonClassMap.RegisterClassMap<RecurringPayment>(cm => { 
                cm.AutoMap(); 
                cm.SetDiscriminator("RecurringPayment");
                //Type type = typeof(RecurringPayment);
                //List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                //foreach (PropertyInfo propertyInfo in propertyInfos)
                //{
                //    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                //}
            });
            
            //Order
            BsonClassMap.RegisterClassMap<Order>(cm =>
            {
                Type type = typeof(Order);
                cm.AutoMap(); cm.SetDiscriminator("Order");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });

            //NewsItem
            BsonClassMap.RegisterClassMap<NewsItem>(cm =>
            {
                Type type = typeof(NewsItem);
                cm.AutoMap(); 
                cm.SetDiscriminator("NewsItem");
                cm.m(c => c.Language);
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //QueuedEmail
            BsonClassMap.RegisterClassMap<QueuedEmail>(cm =>
            {
                Type type = typeof(QueuedEmail);
                cm.AutoMap(); cm.SetDiscriminator("QueuedEmail");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //ScheduleTask
            BsonClassMap.RegisterClassMap<ScheduleTask>(cm =>
            {
                Type type = typeof(ScheduleTask);
                cm.AutoMap(); cm.SetDiscriminator("ScheduleTask");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });



            //Poll
            BsonClassMap.RegisterClassMap<Poll>(cm =>
            {
                Type type = typeof(Poll);
                cm.AutoMap(); cm.SetDiscriminator("Poll");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //ForumTopic
            BsonClassMap.RegisterClassMap<ForumTopic>(cm =>
            {
                Type type = typeof(ForumTopic);
                cm.AutoMap(); cm.SetDiscriminator("ForumTopic");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });



            //Forum
            BsonClassMap.RegisterClassMap<Forum>(cm =>
            {
                Type type = typeof(Forum);
                cm.AutoMap(); cm.SetDiscriminator("Forum");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //Discount
            BsonClassMap.RegisterClassMap<Discount>(cm =>
            {
                Type type = typeof(Discount);
                cm.AutoMap(); cm.SetDiscriminator("Discount");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //Customer
            BsonClassMap.RegisterClassMap<Customer>(cm =>
            {
                Type type = typeof(Customer);
                cm.AutoMap(); cm.SetDiscriminator("Customer");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });


            //Shipment
            BsonClassMap.RegisterClassMap<Shipment>(cm =>
            {
                Type type = typeof(Shipment);
                cm.AutoMap(); cm.SetDiscriminator("Shipment");
                List<PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType.FullName.Contains("System.DateTime") && p.PropertyType.GUID == new Guid("9a9177c7-cf5f-31ab-8495-96f58ac5df3a")).ToList<PropertyInfo>();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    cm.GetMemberMap(propertyInfo.Name).SetSerializer(new DateTimeSerializer());
                }
            });




            BsonClassMap.RegisterClassMap<Site>(cm => { cm.AutoMap(); cm.SetDiscriminator("Site"); });
            BsonClassMap.RegisterClassMap<Picture>(cm => { cm.AutoMap(); cm.SetDiscriminator("Picture"); });
            BsonClassMap.RegisterClassMap<Download>(cm => { cm.AutoMap(); cm.SetDiscriminator("Download"); });

            BsonClassMap.RegisterClassMap<MeasureDimension>(cm => { cm.AutoMap(); cm.SetDiscriminator("MeasureDimension"); });
            BsonClassMap.RegisterClassMap<MeasureWeight>(cm => { cm.AutoMap(); cm.SetDiscriminator("MeasureWeight"); });
            BsonClassMap.RegisterClassMap<TaxCategory>(cm => { cm.AutoMap(); cm.SetDiscriminator("TaxCategory"); });

            BsonClassMap.RegisterClassMap<Language>(cm => 
            { 
                cm.AutoMap(); 
                cm.SetDiscriminator("Language");
                cm.GetMemberMap(c => c.LocaleStringResources).SetIsRequired(false);
                cm.UnmapProperty(c => c.LocaleStringResources);
            });
            
            
            BsonClassMap.RegisterClassMap<LocaleStringResource>(cm => 
            { 
                cm.AutoMap();
                cm.SetDiscriminator("LocaleStringResource");
                cm.UnmapProperty(c => c.Language);
            });

            


            BsonClassMap.RegisterClassMap<Currency>(cm => { cm.AutoMap(); cm.SetDiscriminator("Currency"); });
            
            BsonClassMap.RegisterClassMap<CustomerRole>(cm => { cm.AutoMap(); cm.SetDiscriminator("CustomerRole"); });
            BsonClassMap.RegisterClassMap<SpecificationAttribute>(cm => { cm.AutoMap(); cm.SetDiscriminator("SpecificationAttribute"); });
            BsonClassMap.RegisterClassMap<ProductAttribute>(cm => { cm.AutoMap(); cm.SetDiscriminator("ProductAttribute"); });
            BsonClassMap.RegisterClassMap<Category>(cm => { cm.AutoMap(); cm.SetDiscriminator("Category"); });
            BsonClassMap.RegisterClassMap<Manufacturer>(cm => { cm.AutoMap(); cm.SetDiscriminator("Manufacturer"); });
            BsonClassMap.RegisterClassMap<Product>(cm => { cm.AutoMap(); cm.SetDiscriminator("Product"); });
            BsonClassMap.RegisterClassMap<UrlRecord>(cm => { cm.AutoMap(); cm.SetDiscriminator("UrlRecord"); });
            BsonClassMap.RegisterClassMap<RelatedProduct>(cm => { cm.AutoMap(); cm.SetDiscriminator("RelatedProduct"); });
            BsonClassMap.RegisterClassMap<EmailAccount>(cm => { cm.AutoMap(); cm.SetDiscriminator("EmailAccount"); });
            BsonClassMap.RegisterClassMap<MessageTemplate>(cm => { cm.AutoMap(); cm.SetDiscriminator("MessageTemplate"); });
            BsonClassMap.RegisterClassMap<ForumGroup>(cm => { cm.AutoMap(); cm.SetDiscriminator("ForumGroup"); });
            
            BsonClassMap.RegisterClassMap<Country>(cm => { cm.AutoMap(); cm.SetDiscriminator("Country"); });
            BsonClassMap.RegisterClassMap<StateProvince>(cm => { cm.AutoMap(); cm.SetDiscriminator("StateProvince"); });
            
            
            BsonClassMap.RegisterClassMap<Topic>(cm => { cm.AutoMap(); cm.SetDiscriminator("Topic"); });
            
            
            BsonClassMap.RegisterClassMap<ShippingMethod>(cm => { cm.AutoMap(); cm.SetDiscriminator("ShippingMethod"); });
            BsonClassMap.RegisterClassMap<ActivityLogType>(cm => { cm.AutoMap(); cm.SetDiscriminator("ActivityLogType"); });
            BsonClassMap.RegisterClassMap<ProductTag>(cm => { cm.AutoMap(); cm.SetDiscriminator("ProductTag"); });
            BsonClassMap.RegisterClassMap<ProductTemplate>(cm => { cm.AutoMap(); cm.SetDiscriminator("ProductTemplate"); });
            BsonClassMap.RegisterClassMap<CategoryTemplate>(cm => { cm.AutoMap(); cm.SetDiscriminator("CategoryTemplate"); });
            BsonClassMap.RegisterClassMap<ManufacturerTemplate>(cm => { cm.AutoMap(); cm.SetDiscriminator("ManufacturerTemplate"); });
            
            BsonClassMap.RegisterClassMap<Setting>(cm => { cm.AutoMap(); cm.SetDiscriminator("Setting"); });
            BsonClassMap.RegisterClassMap<GenericAttribute>(cm => { cm.AutoMap(); cm.SetDiscriminator("GenericAttribute"); });
            BsonClassMap.RegisterClassMap<Log>(cm => { cm.AutoMap(); cm.SetDiscriminator("Log"); });
            BsonClassMap.RegisterClassMap<PermissionRecord>(cm => { cm.AutoMap(); cm.SetDiscriminator("PermissionRecord"); });


        }
    }
}
